﻿local Aura = AceLibrary("SpecialEvents-Aura-2.0")
local Dewdrop = AceLibrary("Dewdrop-2.0")
local RL = AceLibrary("Roster-2.1")
local BS = AceLibrary("Babble-Spell-2.2")
local BC = AceLibrary("Babble-Class-2.2")

local L = AceLibrary("AceLocale-2.2"):new("GridStatus_MoreAuras")

L:RegisterTranslations("enUS", function () return {
	["More Auras"] = true,
	["Debuff type: %s"] = true,
	["Poison"] = true,
	["Disease"] = true,
	["Magic"] = true,
	["Curse"] = true,
	["Add new Buff"] = true,
	["Adds a new buff to the status module"] = true,
	["Add new Debuff"] = true,
	["Adds a new debuff to the status module"] = true,
	["Delete (De)buff"] = true,
	["Deletes an existing debuff from the status module"] = true,
	["Remove %s from the menu"] = true,
	["Debuff: %s"] = true,
	["Buff: %s"] = true,
	["Class Filter"] = true,
	["Show status for the selected classes."] = true,
	["Show on %s."] = true,
	["Show if missing"] = true,
	["Display status only if the buff is not active."] = true,
} end)

local mod = GridStatus:NewModule("GridStatus_MoreAuras")
mod.menuName = L["More Auras"]


mod.defaultDB = {
	debug = false,
	debuffs = {
		[BS["Mortal Strike"]] = true,
	},
	buffs = {
		[BS["Power Word: Shield"]] = true,
	},
}


function mod:OnInitialize()
	self.super.OnInitialize(self)

	self:RegisterStatuses()
end


function mod:RegisterStatuses()
	self:RegisterStatus("important_debuff", L["Important Debuffs"], self.name)
	self:RegisterStatus("important_buff", L["Important Buffs"], self.name)
end


function mod:UnregisterStatuses()
	for status, moduleName, desc in self.core:RegisteredStatusIterator() do
		if moduleName == self.name then
			self:UnregisterStatus(status)
			self.options.args[status] = nil
		end
	end
end


function mod:OnEnable()
	self.debugging = self.db.profile.debug
	self:RegisterEvent("SpecialEvents_UnitDebuffGained")
	self:RegisterEvent("SpecialEvents_UnitDebuffLost")
	self:RegisterEvent("SpecialEvents_UnitBuffGained")
	self:RegisterEvent("SpecialEvents_UnitBuffLost")
	self:RegisterEvent("Grid_UnitJoined")
	self:RegisterEvent("Grid_UnitDeath", "ClearAuras")
	self:Debug("OnEnable")

	self:UpdateAllUnitAuras()
end


function mod:Reset()
	self.super.Reset(self)

	self:UnregisterStatuses()
	self:RegisterStatuses()
end


do
	local remove_header = {
		type = "header",
		order = 199,
	}

	local add_header = {
		type = "header",
		order = 299,
	}

	function mod:OptionsForStatus(status, list)
		local auraOptions = {
		}
	
		if list then
			auraOptions.h1 = remove_header
			for index, aura in ipairs(list) do
				auraOptions["aura"..index] = {
					type = "execute",
					name = aura,
					order = 200,
					desc = string.format(L["Remove %s from the menu"], aura),
					func = function() return self:DeleteAuraFromList(status, aura) end
				}
			end

			auraOptions.h2 = add_header
			auraOptions.add = {
				type = "text",
				name = L["Add new Aura"],
				desc = L["Adds a new aura to the list"],
				get = false,
				usage = "<aura name>",
				set = function(v) self:AddAuraToList(status, v) end,
				order = 300
			}
		end

		return auraOptions
	end
end

function mod:AddAuraToList(status, aura)

	local list = self.db.profile[status].list
	list[aura] = true
	
	self.options.args[status]["aura"..#list] = {
		type = "execute",
		name = aura,
		order = 200,
		desc = string.format(L["Remove %s from the menu"], aura),
		func = function() return self:DeleteAuraFromList(status, aura) end
	}
end


function mod:DeleteAuraFromList(status, aura)
	local p = self.db.profile[status]
	local list = p.list
	local index
	for i, a in ipairs(list) do
		if a == aura then
			index = i
			break
		end
	end
	if not index then return end
	local auraOptions = self.options.args[status]
	if index == #list then
		list[index] = nil
		auraOptions["aura"..index] = nil
	else
		table.remove(list, index)
		for i = index, #list do
			auraOptions["aura"..i] = auraOptions["aura"..(i + 1)]
		end
	end
end


function mod:UpdateAllUnitAuras()
	for u in RL:IterateRoster() do
		self:ClearAuras(u.unitname)
		self:ScanUnitAuras(u.unitid)
	end
end


function mod:Grid_UnitJoined(name, unit)
	self:ClearAuras(name)
	self:ScanUnitAuras(unit)
end


function mod:ScanUnitAuras(unit)
	self:Debug("ScanUnitAuras", unit)

	for buff, index, apps, tex, rank in Aura:BuffIter(unit) do
		self:SpecialEvents_UnitBuffGained(unit, buff, index, apps, tex, rank)
	end

	for debuff, apps, type, tex, rank, index in Aura:DebuffIter(unit) do
		self:SpecialEvents_UnitDebuffGained(unit, debuff, apps, type, tex, rank, index)
	end
end


function mod:SpecialEvents_UnitDebuffGained(unit, debuff)
	-- check if this is a specific debuff or a debuff type

	local list = self.db.profile.

	if not (settings and settings.enable) then return end

	local u = RL:GetUnitObjectFromUnit(unit)

	-- ignore the event if we're skipping this class or if we don't have a valid
	-- unit object
	if not u or settings[strlower(u.class)] == false then return end

	self:Debug(unit, "gained", status, debuffNameStatus, tex)

	self.core:SendStatusGained(u.name,
				   status,
				   settings.priority,
				   (settings.range and 30),
				   settings.color,
				   settings.text,
				   nil,
				   nil,
				   tex)
end


function mod:SpecialEvents_UnitDebuffLost(unit, debuff, apps, type, tex, rank)
	local debuffNameStatus = statusForSpell(debuff, false)
	local debuffTypeStatus = "debuff_" .. strlower(type)

	local name = UnitName(unit)

	if self.db.profile[debuffNameStatus] then
		if not Aura:UnitHasDebuff(unit, debuff) then
			self:Debug(unit, "lost", debuffNameStatus)
			self.core:SendStatusLost(name, debuffNameStatus)
		end
	end

	if not Aura:UnitHasDebuffType(unit, type) then
		self:Debug(unit, "lost", debuffTypeStatus, debuffNameStatus)
		self.core:SendStatusLost(name, debuffTypeStatus)
	end
end


function mod:SpecialEvents_UnitBuffGained(unit, buff, index, apps, tex, rank)
	return self:UnitBuff(unit, true, buff, tex)
end

function mod:SpecialEvents_UnitBuffLost(unit, buff, apps, tex, rank)
	return self:UnitBuff(unit, false, buff, tex)
end

function mod:UnitBuff(unit, gained, buff, tex)
	local buffNameStatus = statusForSpell(buff, true)
	local settings = self.db.profile[buffNameStatus]
	if not (settings and settings.enable) then return end

	local u = RL:GetUnitObjectFromUnit(unit)

	-- ignore the event if we're skipping this class or if we don't have a valid
	-- unit object
	if not u or settings[strlower(u.class)] == false then return end

	if gained then
		self:Debug("gained", buffNameStatus, tex)
		
		if settings.missing then
			self:Debug("sending lost", buffNameStatus)
			self.core:SendStatusLost(u.name, buffNameStatus)
		else
			self:Debug("sending gained", buffNameStatus)
			self.core:SendStatusGained(u.name,
						   buffNameStatus,
						   settings.priority,
						   (settings.range and 40),
						   settings.color,
						   settings.text,
						   nil,
						   nil,
						   tex)
		end
	else
		self:Debug("lost", buffNameStatus, tex)
		if not Aura:UnitHasBuff(unit, buff) then
			if settings.missing then
				self:Debug("sending gained", buffNameStatus)
				self.core:SendStatusGained(u.name,
							   buffNameStatus,
							   settings.priority,
							   (settings.range and 40),
							   settings.color,
							   settings.text,
							   nil,
							   nil,
							   tex)
			else
				self:Debug("sending lost", buffNameStatus)
				self.core:SendStatusLost(UnitName(unit), buffNameStatus)
			end
		end
	end
end


function mod:ClearAuras(unitname)
	self:Debug("ClearAuras", unitname)
	local u = RL:GetUnitObjectFromName(unitname)

	for status, moduleName, desc in self.core:RegisteredStatusIterator() do
		if moduleName == self.name then
			local settings = self.db.profile[status]
			self:Debug("clearing", status, settings.missing)

			if settings.enable and settings.missing and u and settings[strlower(u.class)] then
				self.core:SendStatusGained(unitname,
							   status,
							   settings.priority,
							   (settings.range and 40),
							   settings.color,
							   settings.text,
							   nil,
							   nil,
							   BS:GetSpellIcon(settings.text))
			else
				self.core:SendStatusLost(unitname, status)
			end
		end
	end
end
